Image Tools


Registration of an Image Stack

part of

pyTEMlib, a pycroscopy library

Notebook by

Gerd Duscher

Materials Science & Engineering Joint Institute of Advanced Materials The University of Tennessee, Knoxville

An Introduction into tegistration of an image stack.

This works also on Google Colab.

Prerequesites

Install pyTEMlib

If you have not done so in the Introduction Notebook, please test and install pyTEMlib and other important packages with the code cell below.

[41]:
import sys
from pkg_resources import get_distribution, DistributionNotFound

def test_package(package_name):
    """Test if package exists and returns version or -1

    This test does not require a loading of the packages
    """
    try:
        version = (get_distribution(package_name).version)
    except (DistributionNotFound, ImportError) as err:
        version = '-1'
    return version

# Colab setup ------------------
if 'google.colab' in sys.modules:
    !pip install git+https://github.com/pycroscopy/pyNSID/ -q
    !pip install git+https://github.com/pycroscopy/sidpy/ -q
    !pip install git+https://github.com/pycroscopy/pyTEMlib/ -q
else:
    # pyTEMlib setup ------------------
    if test_package('pyTEMlib') < '0.2022.1.6':
        !{sys.executable} -m pip install  --upgrade git+https://github.com/pycroscopy/pyTEMlib
        print('installed pyTEMlib')
    if test_package('sidpy') < '0.0.4':
        !{sys.executable} -m pip install  --upgrade sidpy -q
        print('installed sidpy')
    if test_package('pyNSID') < '0.0.1':
        !{sys.executable} -m pip install  --upgrade  git+https://github.com/pycroscopy/pyNSID -q
        print('Installed pyNSID')
# ------------------------------
print('done')
Collecting git+https://github.com/pycroscopy/pyTEMlibinstalled pyTEMlib
  Cloning https://github.com/pycroscopy/pyTEMlib to c:\users\gduscher\appdata\local\temp\pip-req-build-dl_qj9wm
Requirement already satisfied: scipy in c:\users\gduscher\anaconda3\lib\site-packages (from pyTEMlib==0.2021.1.5) (1.5.3)
Requirement already satisfied: numpy in c:\users\gduscher\anaconda3\lib\site-packages (from pyTEMlib==0.2021.1.5) (1.19.4)
Requirement already satisfied: pillow in c:\users\gduscher\anaconda3\lib\site-packages (from pyTEMlib==0.2021.1.5) (8.0.1)
Requirement already satisfied: simpleITK in c:\users\gduscher\anaconda3\lib\site-packages (from pyTEMlib==0.2021.1.5) (2.0.2)
Requirement already satisfied: ase in c:\users\gduscher\anaconda3\lib\site-packages (from pyTEMlib==0.2021.1.5) (3.20.1)
Requirement already satisfied: pyNSID in c:\users\gduscher\anaconda3\lib\site-packages (from pyTEMlib==0.2021.1.5) (0.0.1)
Requirement already satisfied: matplotlib>=2.0.0 in c:\users\gduscher\anaconda3\lib\site-packages (from ase->pyTEMlib==0.2021.1.5) (3.3.3)
Requirement already satisfied: kiwisolver>=1.0.1 in c:\users\gduscher\anaconda3\lib\site-packages (from matplotlib>=2.0.0->ase->pyTEMlib==0.2021.1.5) (1.3.1)
Requirement already satisfied: cycler>=0.10 in c:\users\gduscher\anaconda3\lib\site-packages (from matplotlib>=2.0.0->ase->pyTEMlib==0.2021.1.5) (0.10.0)
Requirement already satisfied: python-dateutil>=2.1 in c:\users\gduscher\anaconda3\lib\site-packages (from matplotlib>=2.0.0->ase->pyTEMlib==0.2021.1.5) (2.8.1)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in c:\users\gduscher\anaconda3\lib\site-packages (from matplotlib>=2.0.0->ase->pyTEMlib==0.2021.1.5) (2.4.7)
Requirement already satisfied: six in c:\users\gduscher\anaconda3\lib\site-packages (from cycler>=0.10->matplotlib>=2.0.0->ase->pyTEMlib==0.2021.1.5) (1.15.0)
Requirement already satisfied: ipywidgets>=5.2.2 in c:\users\gduscher\anaconda3\lib\site-packages (from pyNSID->pyTEMlib==0.2021.1.5) (7.6.0)
Requirement already satisfied: h5py>=2.6.0 in c:\users\gduscher\anaconda3\lib\site-packages (from pyNSID->pyTEMlib==0.2021.1.5) (3.1.0)
Requirement already satisfied: toolz in c:\users\gduscher\anaconda3\lib\site-packages (from pyNSID->pyTEMlib==0.2021.1.5) (0.11.1)
Requirement already satisfied: ipython>=6.0 in c:\users\gduscher\anaconda3\lib\site-packages (from pyNSID->pyTEMlib==0.2021.1.5) (7.19.0)
Requirement already satisfied: sidpy>=0.0.2 in c:\users\gduscher\anaconda3\lib\site-packages (from pyNSID->pyTEMlib==0.2021.1.5) (0.0.4)
Requirement already satisfied: dask>=0.10 in c:\users\gduscher\anaconda3\lib\site-packages (from pyNSID->pyTEMlib==0.2021.1.5) (2020.12.0)
Requirement already satisfied: cytoolz in c:\users\gduscher\anaconda3\lib\site-packages (from pyNSID->pyTEMlib==0.2021.1.5) (0.11.0)
Requirement already satisfied: pyyaml in c:\users\gduscher\anaconda3\lib\site-packages (from dask>=0.10->pyNSID->pyTEMlib==0.2021.1.5) (5.3.1)
Requirement already satisfied: pygments in c:\users\gduscher\anaconda3\lib\site-packages (from ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (2.7.3)
Requirement already satisfied: backcall in c:\users\gduscher\anaconda3\lib\site-packages (from ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (0.2.0)
Requirement already satisfied: colorama in c:\users\gduscher\anaconda3\lib\site-packages (from ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (0.4.4)
Requirement already satisfied: setuptools>=18.5 in c:\users\gduscher\anaconda3\lib\site-packages (from ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (51.1.1)

Requirement already satisfied: pickleshare in c:\users\gduscher\anaconda3\lib\site-packages (from ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (0.7.5)
Requirement already satisfied: decorator in c:\users\gduscher\anaconda3\lib\site-packages (from ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (4.4.2)
Requirement already satisfied: traitlets>=4.2 in c:\users\gduscher\anaconda3\lib\site-packages (from ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (5.0.5)
Requirement already satisfied: jedi>=0.10 in c:\users\gduscher\anaconda3\lib\site-packages (from ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (0.17.2)
Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in c:\users\gduscher\anaconda3\lib\site-packages (from ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (3.0.8)
Requirement already satisfied: ipykernel>=4.5.1 in c:\users\gduscher\anaconda3\lib\site-packages (from ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (5.4.2)
Requirement already satisfied: nbformat>=4.2.0 in c:\users\gduscher\anaconda3\lib\site-packages (from ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (5.0.8)
Requirement already satisfied: widgetsnbextension~=3.5.0 in c:\users\gduscher\anaconda3\lib\site-packages (from ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (3.5.1)
Requirement already satisfied: jupyterlab-widgets>=1.0.0 in c:\users\gduscher\anaconda3\lib\site-packages (from ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (1.0.0)
Requirement already satisfied: jupyter-client in c:\users\gduscher\anaconda3\lib\site-packages (from ipykernel>=4.5.1->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (6.1.7)
Requirement already satisfied: tornado>=4.2 in c:\users\gduscher\anaconda3\lib\site-packages (from ipykernel>=4.5.1->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (6.1)
Requirement already satisfied: parso<0.8.0,>=0.7.0 in c:\users\gduscher\anaconda3\lib\site-packages (from jedi>=0.10->ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (0.7.0)
Requirement already satisfied: ipython-genutils in c:\users\gduscher\anaconda3\lib\site-packages (from nbformat>=4.2.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.2.0)
Requirement already satisfied: jsonschema!=2.5.0,>=2.4 in c:\users\gduscher\anaconda3\lib\site-packages (from nbformat>=4.2.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (3.2.0)
Requirement already satisfied: jupyter-core in c:\users\gduscher\anaconda3\lib\site-packages (from nbformat>=4.2.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (4.7.0)
Requirement already satisfied: pyrsistent>=0.14.0 in c:\users\gduscher\anaconda3\lib\site-packages (from jsonschema!=2.5.0,>=2.4->nbformat>=4.2.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.17.3)
Requirement already satisfied: attrs>=17.4.0 in c:\users\gduscher\anaconda3\lib\site-packages (from jsonschema!=2.5.0,>=2.4->nbformat>=4.2.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (20.3.0)
Requirement already satisfied: wcwidth in c:\users\gduscher\anaconda3\lib\site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython>=6.0->pyNSID->pyTEMlib==0.2021.1.5) (0.2.5)
Requirement already satisfied: joblib>=0.11.0 in c:\users\gduscher\anaconda3\lib\site-packages (from sidpy>=0.0.2->pyNSID->pyTEMlib==0.2021.1.5) (1.0.0)
Requirement already satisfied: psutil in c:\users\gduscher\anaconda3\lib\site-packages (from sidpy>=0.0.2->pyNSID->pyTEMlib==0.2021.1.5) (5.8.0)
Requirement already satisfied: notebook>=4.4.1 in c:\users\gduscher\anaconda3\lib\site-packages (from widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (6.1.6)
Requirement already satisfied: prometheus-client in c:\users\gduscher\anaconda3\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.9.0)
Requirement already satisfied: jinja2 in c:\users\gduscher\anaconda3\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (2.11.2)
Requirement already satisfied: Send2Trash in c:\users\gduscher\anaconda3\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (1.5.0)
Requirement already satisfied: pyzmq>=17 in c:\users\gduscher\anaconda3\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (20.0.0)
Requirement already satisfied: nbconvert in c:\users\gduscher\anaconda3\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (6.0.7)
Requirement already satisfied: argon2-cffi in c:\users\gduscher\anaconda3\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (20.1.0)
Requirement already satisfied: terminado>=0.8.3 in c:\users\gduscher\anaconda3\lib\site-packages (from notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.9.1)
Requirement already satisfied: pywin32>=1.0 in c:\users\gduscher\anaconda3\lib\site-packages (from jupyter-core->nbformat>=4.2.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (228)
Requirement already satisfied: pywinpty>=0.5 in c:\users\gduscher\anaconda3\lib\site-packages (from terminado>=0.8.3->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.5.7)
Requirement already satisfied: cffi>=1.0.0 in c:\users\gduscher\anaconda3\lib\site-packages (from argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (1.14.4)
Requirement already satisfied: pycparser in c:\users\gduscher\anaconda3\lib\site-packages (from cffi>=1.0.0->argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (2.20)
Requirement already satisfied: MarkupSafe>=0.23 in c:\users\gduscher\anaconda3\lib\site-packages (from jinja2->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (1.1.1)
Requirement already satisfied: mistune<2,>=0.8.1 in c:\users\gduscher\anaconda3\lib\site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.8.4)
Requirement already satisfied: jupyterlab-pygments in c:\users\gduscher\anaconda3\lib\site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.1.2)
Requirement already satisfied: pandocfilters>=1.4.1 in c:\users\gduscher\anaconda3\lib\site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (1.4.2)
Requirement already satisfied: testpath in c:\users\gduscher\anaconda3\lib\site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.4.4)
Requirement already satisfied: entrypoints>=0.2.2 in c:\users\gduscher\anaconda3\lib\site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.3)
Requirement already satisfied: defusedxml in c:\users\gduscher\anaconda3\lib\site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.6.0)
Requirement already satisfied: bleach in c:\users\gduscher\anaconda3\lib\site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (3.2.1)
Requirement already satisfied: nbclient<0.6.0,>=0.5.0 in c:\users\gduscher\anaconda3\lib\site-packages (from nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.5.1)
Requirement already satisfied: nest-asyncio in c:\users\gduscher\anaconda3\lib\site-packages (from nbclient<0.6.0,>=0.5.0->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (1.4.3)
Requirement already satisfied: async-generator in c:\users\gduscher\anaconda3\lib\site-packages (from nbclient<0.6.0,>=0.5.0->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (1.10)
Requirement already satisfied: packaging in c:\users\gduscher\anaconda3\lib\site-packages (from bleach->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (20.8)
Requirement already satisfied: webencodings in c:\users\gduscher\anaconda3\lib\site-packages (from bleach->nbconvert->notebook>=4.4.1->widgetsnbextension~=3.5.0->ipywidgets>=5.2.2->pyNSID->pyTEMlib==0.2021.1.5) (0.5.1)
Building wheels for collected packages: pyTEMlib
  Building wheel for pyTEMlib (setup.py): started
  Building wheel for pyTEMlib (setup.py): finished with status 'done'
  Created wheel for pyTEMlib: filename=pyTEMlib-0.2021.1.5-py2.py3-none-any.whl size=466391 sha256=d967b9aab9ed93259b9119daa86d153bc2bd30093f37cb21edbb638cda1daa8f
  Stored in directory: C:\Users\gduscher\AppData\Local\Temp\pip-ephem-wheel-cache-e5a51eox\wheels\26\8c\23\58dec6616094458bcb9a74aabde477d11aab647055b048390d
Successfully built pyTEMlib
Installing collected packages: pyTEMlib
  Attempting uninstall: pyTEMlib
    Found existing installation: pyTEMlib 0.2021.1.4
    Uninstalling pyTEMlib-0.2021.1.4:
      Successfully uninstalled pyTEMlib-0.2021.1.4
Successfully installed pyTEMlib-0.2021.1.5
Installed pyNSID
done

Loading of necessary libraries

Please note, that we only need to load the pyTEMlib library, which is based on sidpy Datsets.

[80]:
import sys
if 'google.colab' in sys.modules:
    import numpy as np
else:
    %pylab --no-import-all notebook
    %gui qt

# Import libraries from pyTEMlib
import sidpy
import pyTEMlib
import pyTEMlib.file_tools as ft     # File input/ output library
import pyTEMlib.image_tools as it
import pyTEMlib.viz

# For archiving reasons it is a good idea to print the version numbers out at this point
print('pyTEM version: ',pyTEMlib.__version__)
import pyNSID
__notebook__ = '2_Image_Registration'
__notebook_version__ = '2021_01_04'

Populating the interactive namespace from numpy and matplotlib
pyTEM version:  0.2021.01.05

Open File

Choose Mode of File Selection

Here you can choose between a FileWidget or a FileDialog to select a file.

The FileWidget will use display the name of a Nion File like in NionSwift and is often the better way for those files.

The FileDialog does not work on Google Colab and thus the FileWidget will be used automatically.

[81]:
# ---- INPUT choices ---- #
Open_Nion_Directory = True
bokeh_plot = True  # using bokeh to plot or not
# ------------------------#

drive_directory = ft.get_last_path()
if 'google.colab' in sys.modules:
    from google.colab import drive
    drive.mount("/content/drive")
    Open_Nion_Directory = True
    drive_directory = 'drive/MyDrive/'
    bokeh_plot = True
elif not ft.QT_available:
    Open_Nion_Directory = True

if bokeh_plot == True:
    from bokeh.plotting import figure, show, output_notebook
    from bokeh.models import LinearAxis, Range1d
    output_notebook()

### Open file widget
if Open_Nion_Directory:
    file_widget = pyTEMlib.file_tools.FileWidget(drive_directory)
Loading BokehJS ...

Load File

If you did not choose the file widget a File Dialog window will open in the next code cell.

Otherewise just select a File in the widget above

These datasets are stored in the pyNSID data format (extension: hf5) automatically.

All results can be stored in that file.

[82]:
try:
    current_channel.file.close()
except:
    pass

if Open_Nion_Directory:
    dataset = pyTEMlib.file_tools.open_file(file_widget.file_name)
else:
    dataset = pyTEMlib.file_tools.open_file()

current_channel = dataset.h5_dataset.parent.parent

found_log = False
for key in current_channel:
    if 'Log' in key:
        if found_log == False:
            print('Stored Results:')
        found_log = True
        for key2 in current_channel[key]:
            if '_metadata' in current_channel[key][key2]:
                if 'analysis' in current_channel[key][key2]['_metadata'].attrs.keys():
                    print(f" - {key} includes analysis: {current_channel[key][key2]['_metadata'].attrs['analysis']}")

if bokeh_plot:
    p = pyTEMlib.viz.plot(dataset)
    show(p)
else:
    dataset.plot()
dataset

C:\Users\gduscher\Anaconda3\lib\site-packages\pyNSID\io\nsi_reader.py:29: FutureWarning: This Reader will eventually be moved to the ScopeReaders package. Be prepared to change your import statements
  warn('This Reader will eventually be moved to the ScopeReaders package'
C:\Users\gduscher\Anaconda3\lib\site-packages\numpy\core\fromnumeric.py:87: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
  return ufunc.reduce(obj, axis, dtype, out, **passkwargs)
Stored Results:
 - Log_000 includes analysis: rigid sub-pixel registration
 - Log_001 includes analysis: non-rigid demon registration
[82]:
Array Chunk
Bytes 26.21 MB 26.21 MB
Shape (25, 512, 512) (25, 512, 512)
Count 1 Tasks 1 Chunks
Type float32 numpy.ndarray
512 512 25

First: Rigid Registration

If this is an image stack we need to register and add the images.

Using sub-pixel accuracy registration determination method of:

Manuel Guizar-Sicairos, Samuel T. Thurman, and James R. Fienup, “Efficient subpixel image registration algorithms,” Optics Letters 33, 156-158 (2008). DOI:10.1364/OL.33.000156

as implemented in phase_cross_correlation function by scikit-image in the registration package.

[83]:
rig_reg_dataset = it.rigid_registration(dataset)


if bokeh_plot:
    p = pyTEMlib.viz.plot(dataset, palette="Cividis256")
    show(p)
else:
    dataset.plot()

Stack contains  25  images, each with 512  pixels in x-direction and  512  pixels in y-direction
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-83-bd29251788d3> in <module>
----> 1 rig_reg_dataset = it.rigid_registration(dataset)
      2
      3
      4 if bokeh_plot:
      5     p = pyTEMlib.viz.plot(dataset, palette="Cividis256")

~\Anaconda3\lib\site-packages\pyTEMlib\image_tools.py in rigid_registration(dataset)
    451             shift = register_translation(fft_fixed, fft_moving, upsample_factor=1000, space='fourier')
    452         else:
--> 453             shift = registration.phase_cross_correlation(fft_fixed, fft_moving, upsample_factor=1000, space='fourier')
    454
    455         fft_fixed = fft_moving

~\Anaconda3\lib\site-packages\skimage\registration\_phase_cross_correlation.py in phase_cross_correlation(reference_image, moving_image, upsample_factor, space, return_error, reference_mask, moving_mask, overlap_ratio)
    237         # Matrix multiply DFT around the current shift estimate
    238         sample_region_offset = dftshift - shifts*upsample_factor
--> 239         cross_correlation = _upsampled_dft(image_product.conj(),
    240                                            upsampled_region_size,
    241                                            upsample_factor,

~\Anaconda3\lib\site-packages\skimage\registration\_phase_cross_correlation.py in _upsampled_dft(data, upsampled_region_size, upsample_factor, axis_offsets)
     68         kernel = ((np.arange(ups_size) - ax_offset)[:, None]
     69                   * fft.fftfreq(n_items, upsample_factor))
---> 70         kernel = np.exp(-im2pi * kernel)
     71
     72         # Equivalent to:

KeyboardInterrupt:

Determine Quality of Rigid Registration

First we fit a polynom of degree polynom_degree onto the drift of x and y separately.

The Nion STEMs are extremly stable and sub_pixel drift per image can be achieved.

A pixel of size 30pm distributed over 512 pixel (the size of many images) is obviously negletible.

[86]:
scale_x = ft.get_slope(rig_reg_dataset.x.values)*1000.
drift = rig_reg_dataset.metadata['drift']
x = np.linspace(0,drift.shape[0]-1,drift.shape[0])

polynom_degree = 2 # 1 is linear fit, 2 is parabolic fit, ...
line_fit_x = np.polyfit(x, drift[:,0], polynom_degree)
poly_x = np.poly1d(line_fit_x)
line_fit_y = np.polyfit(x, drift[:,1], polynom_degree)
poly_y = np.poly1d(line_fit_y)

if bokeh_plot:

    # Seting the params for the first figure.
    s1 = figure(x_axis_type="linear",  plot_width=800,
               plot_height=400)
    # Setting the second y axis range name and range
    s1.extra_y_ranges = {"drift_pm": Range1d(start=drift.min()*scale_x, end=drift.max()*scale_x)}
    # Adding the second axis to the plot.
    s1.add_layout(LinearAxis(y_range_name="drift_pm", axis_label='drift (pm)'), 'right')

    s1.line(x, drift[:,0], legend_label='drift x', color='blue', line_width=2)
    s1.line(x, drift[:,1], legend_label='drift y', color='red', line_width=2)
    s1.line(x, poly_x(x), legend_label='fit drift x', color='green', line_width=2)
    s1.line(x, poly_y(x), legend_label='fit drift y', color='orange', line_width=2)
    s1.xaxis.axis_label = 'time (frame)'
    s1.yaxis.axis_label = 'drift (pixel)'
    s1.legend.click_policy="hide"
    # Show the combined graphs with twin y axes.
    show(s1)

else:
    plt.figure()
    # plot drift and fit of drift
    plt.axhline(color = 'gray')
    plt.plot(x, drift[:,0], label = 'drift x')
    plt.plot(x, drift[:,1], label = 'drift y')
    plt.plot(x, poly_x(x),  label = 'fit_drift_x')
    plt.plot(x, poly_y(x),  label = 'fit_drift_y')
    plt.legend();

    # set second axis in pico meter
    ax_pixels = plt.gca()
    ax_pixels.step(1, 1)
    ax_pm = ax_pixels.twinx()
    x_1, x_2 = ax_pixels.get_ylim()
    ax_pm.set_ylim(x_1*scale_x, x_2*scale_x)

    # add labels
    ax_pixels.set_ylabel('drift [pixels]')
    ax_pm.set_ylabel('drift [pm]')
    ax_pixels.set_xlabel('image number');
    plt.tight_layout()

Contrast

The image should have improved in contrast and signal noise ratio.

[87]:
image = rig_reg_dataset.sum(axis=0)
image = sidpy.Dataset.from_array(image)
image.data_type = 'image'
image.title = 'registered'
image.set_dimension(0,rig_reg_dataset.dim_1)
image.set_dimension(1,rig_reg_dataset.dim_2)

print(f'Contrast = {np.std(np.array(image))/np.average(np.array(image)):.2f}')

if bokeh_plot:
    p = pyTEMlib.viz.plot(image, palette="Cividis256")
    show(p)
else:
    image.plot()

Contrast = 0.50

Log Rigid Registration

We store the croped image-stack and drift-data here.

[17]:
registration_channel = ft.log_results(dataset, rig_reg_dataset)
C:\Users\gduscher\Anaconda3\lib\site-packages\pyNSID\io\hdf_utils.py:349: FutureWarning: validate_h5_dimension may be removed in a future version
  warn('validate_h5_dimension may be removed in a future version',

Non-Rigid Registration

Here we use the Diffeomorphic Demon Non-Rigid Registration as provided by simpleITK.

Please Cite: * simpleITK

and
[18]:
non_rigid_registered = it.demon_registration(rig_reg_dataset)

if bokeh_plot:
    p = plot_stack(non_rigid_registered)
    show(p)
else:
    non_rigid_registered.plot()
:-)
You have successfully completed Diffeomorphic Demons Registration

Contrast

The image should have improved in contrast and signal noise ratio.

[17]:
image = non_rigid_registered.sum(axis=0)
image = sidpy.Dataset.from_array(image)
image.data_type = 'image'
image.title = 'registered'
image.set_dimension(0,non_rigid_registered.dim_1)
image.set_dimension(1,non_rigid_registered.dim_2)

print(f'Contrast = {np.std(np.array(image))/np.average(np.array(image)):.2f}')

if bokeh_plot:
    p = pyTEMlib.viz.plot(image, palette="Cividis256")
    show(p)
else:
    image.plot()
Contrast = 0.39

Log Non-Rigid Registration

[19]:
registration_channel = ft.log_results(dataset, non_rigid_registered)
C:\Users\gduscher\Anaconda3\lib\site-packages\pyNSID\io\hdf_io.py:109: UserWarning: main_data_name should not contain the "-" character. Reformatted name from:Non-Rigid Registration to Non_Rigid Registration
  warn('main_data_name should not contain the "-" character. Reformatted'
C:\Users\gduscher\Anaconda3\lib\site-packages\sidpy\hdf\hdf_utils.py:419: UserWarning: Casting attribute value: <HDF5 dataset "Non_Rigid Registration": shape (25, 508, 508), type "<f8"> of type: <class 'h5py._hl.dataset.Dataset'> to str
  warn('Casting attribute value: {} of type: {} to str'
C:\Users\gduscher\Anaconda3\lib\site-packages\sidpy\hdf\hdf_utils.py:419: UserWarning: Casting attribute value: <sidpy.viz.dataset_viz.ImageStackVisualizer object at 0x00000286A9E02220> of type: <class 'sidpy.viz.dataset_viz.ImageStackVisualizer'> to str
  warn('Casting attribute value: {} of type: {} to str'
C:\Users\gduscher\Anaconda3\lib\site-packages\pyNSID\io\hdf_utils.py:349: FutureWarning: validate_h5_dimension may be removed in a future version
  warn('validate_h5_dimension may be removed in a future version',

A tree-like plot of the file

[27]:
for key in current_channel:
    if 'Log' in key:
        for key2 in current_channel[key]:
            if '_metadata' in current_channel[key][key2]:
                if 'analysis' in current_channel[key][key2]['_metadata'].attrs.keys():
                    print(f"{key} includes analysis: {current_channel[key][key2]['_metadata'].attrs['analysis']}")

ft.h5_tree(dataset)
Log_000 includes analysis: rigid sub-pixel registration
Log_001 includes analysis: non-rigid demon registration
Log_002 includes analysis: rigid sub-pixel registration
Log_003 includes analysis: non-rigid demon registration
/
├ Measurement_000
  ---------------
  ├ Channel_000
    -----------
    ├ Log_000
      -------
      ├ Rigid Registration
        ------------------
        ├ Rigid Registration
        ├ __dict__
          --------
        ├ _axes
          -----
        ├ _metadata
          ---------
        ├ frame
        ├ metadata
          --------
        ├ x
        ├ y
    ├ Log_001
      -------
      ├ Non_Rigid Registration
        ----------------------
        ├ Non_Rigid Registration
        ├ __dict__
          --------
        ├ _axes
          -----
        ├ _metadata
          ---------
        ├ frame
        ├ metadata
          --------
        ├ x
        ├ y
    ├ Log_002
      -------
      ├ Rigid Registration
        ------------------
        ├ Rigid Registration
        ├ __dict__
          --------
        ├ _axes
          -----
        ├ _metadata
          ---------
        ├ frame
        ├ metadata
          --------
        ├ x
        ├ y
    ├ Log_003
      -------
      ├ Non_Rigid Registration
        ----------------------
        ├ Non_Rigid Registration
        ├ __dict__
          --------
        ├ _axes
          -----
        ├ _metadata
          ---------
        ├ frame
        ├ metadata
          --------
        ├ x
        ├ y
    ├ Recording of SuperScan (HAADF)
      ------------------------------
      ├ Log_000
        -------
        ├ Non_Rigid Registration
          ----------------------
          ├ Non_Rigid Registration
          ├ __dict__
            --------
          ├ _axes
            -----
          ├ _metadata
            ---------
          ├ frame
          ├ metadata
            --------
          ├ x
          ├ y
      ├ Recording of SuperScan (HAADF)
      ├ __dict__
        --------
      ├ _axes
        -----
      ├ _original_metadata
        ------------------
      ├ axes
        ----
      ├ frame
      ├ original_metadata
        -----------------
      ├ x
      ├ y

Comparison of Different Dataset

A convenient function to select a dataset (for further processing, visualization or whatever)

[21]:
choose_image = ft.ChooseDataset(dataset.h5_dataset.parent.parent)
C:\Users\gduscher\Anaconda3\lib\site-packages\pyNSID\io\nsi_reader.py:29: FutureWarning: This Reader will eventually be moved to the ScopeReaders package. Be prepared to change your import statements
  warn('This Reader will eventually be moved to the ScopeReaders package'
C:\Users\gduscher\Anaconda3\lib\site-packages\numpy\core\fromnumeric.py:87: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
  return ufunc.reduce(obj, axis, dtype, out, **passkwargs)

The selected dataset can then easily be plotted

[39]:
if bokeh_plot:
    p = pyTEMlib.viz.plot(choose_image.dataset, palette="Cividis256")
    show(p)
else:
    choose_image.dataset.plot()

Close File

let’s close the file but keep the filename

[25]:
filename = registration_channel.file.filename
registration_channel.file.close()
[ ]: